package com.ikas.ai.utils;

import com.google.common.collect.Lists;
import com.ikas.ai.model.BinaryTuple;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.function.BiPredicate;
import java.util.stream.Collectors;

/**
 * @Description: 比较集合
 * @Author: qi.yilai
 * @Date: 2022/3/30 17:02
 * @Version: 1.0.0
 **/
public class ListComparator {

    public static class BiResult<T> {

        public BiResult(List<T> adds, List<T> removes) {
            this.adds = adds;
            this.removes = removes;
        }

        private List<T> adds;

        private List<T> removes;

        public List<T> getAdds() {
            return adds;
        }

        public void setAdds(List<T> adds) {
            this.adds = adds;
        }

        public List<T> getRemoves() {
            return removes;
        }

        public void setRemoves(List<T> removes) {
            this.removes = removes;
        }
    }

    public static class TernaryResult<T> {

        public TernaryResult(List<T> adds, List<T> removes, List<BinaryTuple<T, T>> updates, List<BinaryTuple<T, T>> notChange) {
            this.adds = adds;
            this.removes = removes;
            this.updates = updates;
            this.notChange = notChange;
        }

        private List<T> adds;

        private List<T> removes;

        private List<BinaryTuple<T, T>> updates;

        private List<BinaryTuple<T, T>> notChange;

        public List<T> getAdds() {
            return adds;
        }

        public void setAdds(List<T> adds) {
            this.adds = adds;
        }

        public List<T> getRemoves() {
            return removes;
        }

        public void setRemoves(List<T> removes) {
            this.removes = removes;
        }

        public List<BinaryTuple<T, T>> getUpdates() {
            return updates;
        }

        public List<T> getUpdatesForOld() {
            return updates.stream().map(BinaryTuple::getData1).collect(Collectors.toList());
        }

        public List<T> getUpdatesForNew() {
            return updates.stream().map(BinaryTuple::getData2).collect(Collectors.toList());
        }

        public void setUpdates(List<BinaryTuple<T, T>> updates) {
            this.updates = updates;
        }

        public List<BinaryTuple<T, T>> getNotChange() {
            return notChange;
        }

        public void setNotChange(List<BinaryTuple<T, T>> notChange) {
            this.notChange = notChange;
        }

        public boolean isChanged() {
            return !adds.isEmpty() || !removes.isEmpty() || !updates.isEmpty();
        }
    }

    public static <T> TernaryResult<T> compare(List<T> oldList, List<T> newList, BiPredicate<T, T> uniquePredicate, BiPredicate<T, T> equalsPredicate) {
        if (Objects.isNull(newList)) {
            newList = new ArrayList<>();
        }
        if (Objects.isNull(oldList)) {
            oldList = new ArrayList<>();
        }

        if (CollectionUtils.isEmpty(oldList)) {
            return new TernaryResult<T>(newList, new ArrayList<>(), new ArrayList<>(), new ArrayList<>());
        }

        if (CollectionUtils.isEmpty(newList)) {
            return new TernaryResult<>(new ArrayList<>(), oldList, new ArrayList<>(), new ArrayList<>());
        }
        List<T> copyOld = Lists.newArrayList(oldList);
        List<T> copyNew = Lists.newArrayList(newList);
        List<BinaryTuple> updates = new ArrayList<>();
        List<BinaryTuple> noChange = new ArrayList<>();
        //比较
        Iterator<T> oldIterator = copyOld.iterator();
        while (oldIterator.hasNext()) {
            T t1 = oldIterator.next();
            Iterator<T> newIterator = copyNew.iterator();
            while (newIterator.hasNext()) {
                T t2 = newIterator.next();
                if (uniquePredicate.test(t1, t2)) {
                    oldIterator.remove();
                    newIterator.remove();
                    if (equalsPredicate.test(t1, t2)) {
                        noChange.add(BinaryTuple.of(t1, t2));
                    } else {
                        updates.add(BinaryTuple.of(t1, t2));
                    }
                    break;
                }
            }
        }

        return new TernaryResult(copyNew, copyOld, updates, noChange);
    }


    public static <T> BiResult<T> compare(List<T> oldList, List<T> newList, BiPredicate<T, T> equalsPredicate) {
        if (Objects.isNull(newList)) {
            newList = new ArrayList<>();
        }
        if (Objects.isNull(oldList)) {
            oldList = new ArrayList<>();
        }

        if (CollectionUtils.isEmpty(oldList)) {
            return new BiResult<T>(newList, new ArrayList<>());
        }

        if (CollectionUtils.isEmpty(newList)) {
            return new BiResult<>(new ArrayList<>(), oldList);
        }
        List<T> copyOld = Lists.newArrayList(oldList);
        List<T> copyNew = Lists.newArrayList(newList);

        //比较
        Iterator<T> oldIterator = copyOld.iterator();

        while (oldIterator.hasNext()) {
            T t1 = oldIterator.next();
            Iterator<T> newIterator = copyNew.iterator();
            while (newIterator.hasNext()) {
                T t2 = newIterator.next();
                if (equalsPredicate.test(t1, t2)) {
                    oldIterator.remove();
                    newIterator.remove();
                    break;
                }
            }
        }

        return new BiResult<>(copyNew, copyOld);
    }

    public static <T> BiResult<T> compare(List<T> oldList, List<T> newList) {
        return compare(oldList, newList, new BiPredicate<T, T>() {
            @Override
            public boolean test(T t1, T t2) {
                return t1.equals(t2);
            }
        });
    }
}
